<?php
namespace app\common\traits\model;

use think\db\Query;
use think\Model;

trait RelationHelpers
{
    /**
     * 使用关联查询(别名，Model对象调用)
     * @param mixed  $relation  子模型对象
     * @param null   $alias     子模型别名(单个关联时有效)
     * @param null   $condition 关联条件
     * @param string $joinType  JOIN类型
     * @return Query
     */
    public function useRelationQuery(
        $relation,
        $alias = null,
        $condition = null,
        $joinType = 'INNER'
    ) {
        return $this->handldRelationQuery($this, $relation, $alias, $condition, $joinType);
    }

    /**
     * 使用关联查询(别名，Query对象调用)
     * @param Query  $parent    父Query对象
     * @param null   $relation  子模型对象
     * @param null   $alias     子模型别名(单个关联时有效)
     * @param null   $condition 关联条件
     * @param string $joinType  JOIN类型
     * @return Query
     */
    public function scopeUseRelationQuery(
        Query $parent,
        $relation,
        $alias = null,
        $condition = null,
        $joinType = 'INNER'
    ) {
        return $this->handldRelationQuery($parent->getModel(), $relation, $alias, $condition, $joinType);
    }

    /**
     * 使用关联查询(支持跨库查询)
     * @param Model  $parent        父模型对象
     * @param mixed  $relation      子模型对象
     * @param null   $relationAlias 子模型别名(单个关联时有效)
     * @param null   $condition     关联条件
     * @param string $joinType      JOIN类型
     * @return Query
     *
     * 参数说明
     * parent
     *   父模型对象
     * relation
     *   关联的子模型对象。多个模型关联时，可传入一个数组
     *   数组格式：[Model relation, string|null relationAlias, mixed condition, string joinType]
     * relationAlias
     *   子模型别名。单个关联时有效，如果参数值为 null 时，默认以模型类名作为别名
     * condition
     *   关联条件。可以为字符串或数组， 为数组时每一个元素都是一个关联条件
     * joinType
     *   关联类型。可以为:INNER、LEFT、RIGHT、FULL，不区分大小写，默认为INNER
     *
     * Example
     *   单个关联：(new TestA())->useRelationQuery(new TestB(), null, 'TestA.id=Test.id');
     *   多个关联：(new TestA())->useRelationQuery([[new TestB(), null, 'TestA.id=Test.id']]);
     */
    public function handldRelationQuery(
        Model $parent,
        $relation,
        $relationAlias = null,
        $condition = null,
        $joinType = 'INNER'
    ) {
        list($parentTable, $parentTableAlias) = $this->getJoinTable($parent);
        $aliass = $parent->getOptions('alias');
        $parentTableAlias = empty($alias = $aliass[$parent->getTable()]) ? $parentTableAlias : $alias;
        $this->setTable($parentTable)->alias($parentTableAlias);

        if (empty($condition)) {
            // 如果为组数，则循环调用join
            foreach ($relation as $_join) {
                list($model, $alias, $condition, $joinType) = array_pad($_join, 4, null);
                list($childTable, $tableAlias) = $this->getJoinTable($model);
                $childTableAlias = $alias ?: $tableAlias;
                $this->join(
                    [$childTable => $childTableAlias],
                    $condition,
                    $joinType ?: 'INNER'
                );
            }
        } else {
            list($childTable, $tableAlias) = $this->getJoinTable($relation);
            $childTableAlias = $relationAlias ?: $tableAlias;
            $this->join(
                [$childTable => $childTableAlias],
                $condition,
                $joinType
            );
        }
        return $this;
    }

    /**
     * 获取JOIN表名及别名
     * @param Model $query
     * @return array
     */
    protected function getJoinTable(Model $query)
    {
        $table = $query->getConfig('database') . '.' . $query->getTable();
        $tableAlias = basename(str_replace('\\', '/', get_class($query)));
        return [$table, $tableAlias];
    }
}
